//
// Copyright 2008-2011 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.IO;

namespace YaTools.Yaml
{
    public sealed partial class Dump
    {
        public abstract partial class Decorator
        {
            /// <summary>
            /// Handles mapping tags and tree representation of graphs.
            /// </summary>
            internal class Yaml : Decorator
            {
                private int _anchorCount;

                /// <summary>
                /// Current mapping of tag-prefixes to tag-handles
                /// </summary>
                private readonly List<KeyValuePair<string, string>> _prefixHandlePairs;

                // Cached mappings of tags shorthand to Tag.   Cleared at the end of each document.
                private readonly Dictionary<string, string> _tagCache
                    = new Dictionary<string, string>();

                public Yaml()
                {
                    this.Anchors = new Dictionary<object, string>();
                    this._prefixHandlePairs = new List<KeyValuePair<string, string>>
                                                  {
                                                      new KeyValuePair
                                                          <string, string>(
                                                          Tag.DefaultGlobalPrefix,
                                                          Tag.GlobalHandle)
                                                  };
                }

                // ReSharper disable UnusedMember.Local
                [ContractInvariantMethod]
                private void ObjectInvariant()
                {
                    Contract.Invariant(this._prefixHandlePairs != null);
                }
                // ReSharper restore UnusedMember.Local

                public Dictionary<object, string> Anchors { get; private set; }

                public override bool PresentNode(Dump dump, object value)
                {
                    Contract.Assume(dump != null);
                    if (value == null) return true;

                    string anchor = dump.Anchor; // Transcribe existing anchor if it exists.

                    if (anchor == null)
                    {
                        if (Anchors.TryGetValue(value, out anchor))
                        {
                            if (anchor == null) // Needs an anchor, but doesn't have one...
                            {
                                anchor = TypeDescriptor.GetFullComponentName(value)
                                         ?? (_anchorCount++).ToString();
                                Anchors[value] = anchor;
                            }
                            else // Anchor already assigned, write an alias
                            {
                                PresentAlias(dump, anchor);
                                return false;
                            }
                        }
                    }

                    if (anchor != null)
                    {
                        PresentAnchor(dump, anchor);
                    }
                    return true;
                }

                public override void Preprocess(Dump dump, object value)
                {
                    var presenter = dump._active.Presenter;
                    var decorator = dump._decorator;
                    var writer = dump._writer;

                    try
                    {
                        var myDecorator = new Preprocessor();
                        dump._decorator = myDecorator;
                        dump._writer = TextWriter.Null;
                        dump.DumpValue(value);
                        this.Anchors = myDecorator.GenerateObjectAnchors();
                    }
                    finally
                    {
                        dump._decorator = decorator;
                        dump._writer = writer;
                        dump._active.Presenter = presenter;
                    }
                }

                public override string HandleForTag(string tag)
                {
                    if (tag.StartsWith("!")) return tag;

                    string handle;
                    if (!_tagCache.TryGetValue(tag, out handle))
                    {
                        handle = GenerateHandle(tag);
                        _tagCache[tag] = handle;
                        return handle;
                    }
                    return string.Empty;
                }

                private string GenerateHandle(string tag)
                {
                    Contract.Requires(tag != null);
                    string prefix = string.Empty;
                    string shortcut = null;

                    foreach (var pair in _prefixHandlePairs)
                    {
                        Contract.Assume(pair.Key != null);
                        if (pair.Key.Length > prefix.Length && tag.StartsWith(pair.Key))
                        {
                            prefix = pair.Key;
                            shortcut = pair.Value;
                        }
                    }

                    var prefixLength = prefix.Length;

                    if (prefixLength > 0)
                    {
                        Contract.Assume(prefixLength < tag.Length);
                        return shortcut + tag.Remove(0, prefixLength);
                    }

                    return string.Format("!<{0}>", tag);
                }
            }
        }
    }
}